﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using GGEditor.API.Editors;
using System.Drawing;

namespace GGEditor.API.Samples.TerrainViewer
{
	public class Model : IEditorModel
	{
        private const String dimHeader = "DIM ";
        private const String tilesHeader = "MTXM";
        static readonly List<String> headers;

        static Model()
        {
            headers = new List<String>();
            headers.Add("TYPE");
            headers.Add("VER ");
            headers.Add("DESC");
            headers.Add("OWNR");
            headers.Add("ERA ");
            headers.Add("DIM ");
            headers.Add("UDTA");
            headers.Add("UGRD");
            headers.Add("SIDE");
            headers.Add("SGLD");
            headers.Add("SLBR");
            headers.Add("SOIL");
            headers.Add("AIPL");
            headers.Add("MTXM");
            headers.Add("SGM ");
            headers.Add("OILM");
            headers.Add("REGM");
            headers.Add("UNIT");
        }

        private String filePath;
        public String FilePath { get { return filePath; } }
        
        private int width;
		public int Width { get { return width; } }

		private int height;
		public int Height { get { return height; } }

		private short[,] tiles;
		public short this[int x, int y] 
        { 
            get { return tiles[x, y]; }
            set { tiles[x, y] = value; }
        }

		private Dictionary<String, byte[]> mapData = new Dictionary<string, byte[]>();

		public Model()
		{
			filePath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "config"));
			if (!File.Exists(filePath))
				filePath = Path.GetFullPath(Path.Combine(Path.Combine(
						Path.GetDirectoryName(Application.ExecutablePath),
						"..\\..\\..\\.."), "config"));
			filePath = Path.Combine(filePath, Path.Combine("Samples", "Atols.pud"));

			FileStream file = new FileStream(filePath, FileMode.Open, FileAccess.Read);
			
			BufferedStream reader = new BufferedStream(file);

			while (reader.Position < reader.Length)
				FillSection(reader);
            reader.Close();

			byte[] dimData = mapData[dimHeader];
			width = BitConverter.ToInt16(dimData, 0);
			height = BitConverter.ToInt16(dimData, 2);

			Console.WriteLine("Map Size: " + width + "x" + height);
			tiles = new short[height, width];

			byte[] tilesData = mapData[tilesHeader];
			int count = 0;
			for (int y = 0; y < height; y++)
			{
				for (int x = 0; x < width; x++)
				{
					short tile = BitConverter.ToInt16(tilesData, count);
					tiles[x, y] = tile;
					count += 2;
				}
			}
		}

		private void FillSection(BufferedStream reader)
		{
			byte[] sectionData = new byte[4];
			reader.Read(sectionData, 0, 4);
			String sectionName = System.Text.Encoding.ASCII.GetString(sectionData);

			byte[] lengthData = new byte[4];
			reader.Read(lengthData, 0, 4);
			int length = BitConverter.ToInt32(lengthData, 0);

			byte[] data = new byte[length];
			reader.Read(data, 0, length);

			Console.WriteLine("Section: " + sectionName + " Length: " + length);

			mapData.Add(sectionName, data);
		}

        public void Save(String fileName)
        {
            ConvertTileData();

            FileStream file = new FileStream(fileName, FileMode.Create, FileAccess.Write);
            BufferedStream writer = new BufferedStream(file);

            writer.Seek(0, SeekOrigin.Begin);
            foreach (String header in headers)
            {
                byte[] sectionData = System.Text.Encoding.ASCII.GetBytes(header);
                byte[] data;
                int length;

                if (mapData.ContainsKey(header))
                {
                    data = mapData[header];
                    length = data.Length;
                }
                else
                {
                    data = null;
                    length = 0;
                }

                Console.WriteLine("Writing Section: " + header + " Length: " + length);

                writer.Write(sectionData, 0, 4);
                writer.Write(BitConverter.GetBytes(length), 0, 4);
                if(data != null)
                    writer.Write(data, 0, length);
            }
        }

        //TODO Make sure boundary tile bit fields are done properly (final byte).
        private void ConvertTileData()
        {
            List<byte> tileList = new List<byte>();

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    byte[] tile = BitConverter.GetBytes(tiles[x, y]);
                    tileList.Add(tile[0]);
                    tileList.Add(tile[1]);
                }
            }

            byte[] data = tileList.ToArray();
            if (mapData.ContainsKey(tilesHeader))
                mapData.Remove(tilesHeader);
            mapData.Add(tilesHeader, data);
        }

		private void PrintBytes(byte[] array)
		{
			foreach (byte b in array)
				Console.Write(b + " ");
			Console.WriteLine();
		}

		#region Implementation of IEditorModel

		public event EditorModelChangedHandler ModelChanged;

		#endregion

        public const short LightWater = 0x0010;
        public const short DarkWater = 0x0020; 
        public const short LightCoast = 0x0030; 
        public const short DarkCoast = 0x0040; 
        public const short LightGround = 0x0050; 
        public const short DarkGround = 0x0060; 
        public const short Forest = 0x0070; 
        public const short Mountains = 0x0080; 
        public const short HumanWall = 0x0090; 
        public const short OrcWalls = 0x00a0; 
        public const short HumanWalls = 0x00b0; 
        public const short OrcWalls2 = 0x00c0;

        public const short BoundryOrcWall = 0x0900; 
        public const short BoundryHumanWall = 0x0800; 
        public const short BoundryForestAndGrass = 0x0700; 
        public const short BoundryDarkGrassAndGrass = 0x0600; 
        public const short BoundryCoastAndGrass = 0x0500; 
        public const short BoundryMountAndCoast = 0x0400; 
        public const short BoundryDarkCoastAndCoast = 0x0300;
        public const short BoundryWaterAndCoast = 0x0200;
        public const short BoundryDarkWaterAndWater = 0x0100;

        //Used by the view to get a color representation of the tile
        public Color GetColor(short tile)
        {
            if (tile >= 0x10 && tile < 0x20)
                return Color.CornflowerBlue;
            if (tile >= 0x20 && tile < 0x30)
                return Color.RoyalBlue;

            if (tile >= 0x30 && tile < 0x40)
                return Color.Goldenrod;
            if (tile >= 0x40 && tile < 0x50)
                return Color.DarkGoldenrod;

            if (tile >= 0x50 && tile < 0x60)
                return Color.LightGreen;
            if (tile >= 0x60 && tile < 0x70)
                return Color.Green;

            if (tile >= 0x70 && tile < 0x80)
                return Color.DarkGreen;
            if (tile >= 0x80 && tile < 0x90)
                return Color.DarkGray;


            if (tile >= 0x100 && tile < 0x200)
                return Color.CornflowerBlue;
            if (tile >= 0x200 && tile < 0x300)
                return Color.RoyalBlue;

            if (tile >= 0x300 && tile < 0x400)
                return Color.Goldenrod;
            if (tile >= 0x400 && tile < 0x500)
                return Color.DarkGoldenrod;

            if (tile >= 0x500 && tile < 0x600)
                return Color.LightGreen;
            if (tile >= 0x600 && tile < 0x700)
                return Color.Green;

            if (tile >= 0x700 && tile < 0x800)
                return Color.DarkGreen;
            if (tile >= 0x800 && tile < 0x900)
                return Color.DarkGray;

            return Color.Black;
        }

        public short NextTool(short current)
        {
            switch (current)
            {
                case LightWater:
                    return DarkWater;
                case DarkWater:
                    return LightCoast;
                case LightCoast:
                    return DarkCoast;
                case DarkCoast:
                    return LightGround;
                case LightGround:
                    return DarkGround;
                case DarkGround:
                    return Forest;
                case Forest:
                    return Mountains;
                case Mountains:
                    //return HumanWall;
                /*case HumanWall:
                    return OrcWalls;
                case OrcWalls:
                    return HumanWalls;
                case HumanWalls:
                    return OrcWalls2;
                case OrcWalls2:
                    return BoundryOrcWall;
                case BoundryOrcWall:
                    return BoundryHumanWall;
                case BoundryHumanWall:
                    return BoundryForestAndGrass;
                case BoundryForestAndGrass:
                    return BoundryDarkGrassAndGrass;
                case BoundryDarkGrassAndGrass:
                    return BoundryCoastAndGrass;
                case BoundryCoastAndGrass:
                    return BoundryMountAndCoast;
                case BoundryMountAndCoast:
                    return BoundryDarkCoastAndCoast;
                case BoundryDarkCoastAndCoast:
                    return BoundryWaterAndCoast;
                case BoundryWaterAndCoast:
                    return BoundryDarkWaterAndWater;
                case BoundryDarkWaterAndWater:
                    return LightWater;*/
                default:
                    return LightWater;
            }
        }
	}
}
